package com.hilatest.osgi.test;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.felix.framework.Felix;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;

public class BundleContainer {

	public static final String BUNDLES_FOLDER = "bundles/";
	protected Felix framework;
	
	@Before
	public void initial() throws BundleException{
		final Map<String, String> configMap = new HashMap<String, String>();
	      configMap.put(Constants.FRAMEWORK_STORAGE_CLEAN, "onFirstInit");
	      framework = new Felix(configMap);
	      framework.init();
	      framework.start();
	}

	@Test
	public void testLoadBundles(){
		this.LoadBundles();
	}
	
	public void LoadBundles(){
		Boolean invokeMain = true;
		String[] args = {"org.apache.commons.pool.TestAll"};
		String folder    = BUNDLES_FOLDER;
		this.LoadBundles(folder, invokeMain, args);
	}
	
	protected void beforeInvokeMain(List<Bundle> bundlelist){
		
	}
	
	protected void afterInvokeMain(List<Bundle> bundlelist){
		
	}
	
	public List<Bundle> intallBundles(String folder) throws BundleException{
		List<File> jars = new ArrayList<File>();
		 // Record any bundle with a Main-Class.
	     List<Bundle> bundleList = new ArrayList<Bundle>();
		 if (StringUtils.isEmpty(folder) || !new File(folder).isDirectory()) {
		      System.out.println("Usage: <bundle-directory>");
		    } else {
		      // Look in the specified bundle directory to create a list
		      // of all JAR files to install.
		      File[] files = new File(folder).listFiles();
		      Arrays.sort(files);
		     
		      for (int i = 0; i < files.length; i++){
		        if (files[i].getName().toLowerCase().endsWith(".jar")){
		          jars.add(files[i]);
		        }
		      }
		    }
		 
	     BundleContext ctxt = framework.getBundleContext();
		 for (int i = 0; i < jars.size(); i++) {
	            Bundle b = ctxt.installBundle(((File) jars.get(i)).toURI().toString());
				System.out.println("jar files found:"+((File) jars.get(i)).toURI().toString());
	            bundleList.add(b);
	            
	          }
	      
		 return bundleList;
	}
	public void LoadBundles(String folder, boolean invokeMain,String[] args){
		   try {
	          // Create, configure, and start an OSGi framework instance
	          // using the ServiceLoader to get a factory.
	          
			  Bundle mainBundle = null;
	          List<Bundle> bundleList = this.intallBundles(folder); 
	          // Install bundle JAR files and remember the bundle objects.
	          

	          // Start all installed non-fragment bundles.
	          for (int i = 0; i < bundleList.size(); i++) {
	            if (!isFragment((Bundle) bundleList.get(i))) {
	              Bundle b = bundleList.get(i);
	              ((Bundle) bundleList.get(i)).start();
				  System.out.println("bundle start:"+((Bundle) bundleList.get(i)).getSymbolicName());// Remember "main" bundle.
	              if (b.getHeaders().get("Main-Class") != null) {
	            	  mainBundle = b;
	            }
	            }
	          }

	          
	          if(!invokeMain){
	        	  return;
	          }
	          
	          this.beforeInvokeMain(bundleList);
	          // If a bundle exists with a "Main-Class", then load the class and
	          // invoke its static main method.
	          if (mainBundle != null) {
	            final String mainClassName = (String) mainBundle.getHeaders().get("Main-Class");
				System.out.println("main Bundle:"+mainBundle.getSymbolicName());
				System.out.println("main Class Name:"+mainClassName);
				printStates(bundleList);
	            if (mainClassName != null) {
	              final Class<?> mainClass = mainBundle.loadClass(mainClassName);
	              try {
	                Method method = mainClass.getMethod("main", new Class[] { String[].class });
	                String[] mainArgs = args;
	                method.invoke(null, new Object[] { mainArgs });
	              } catch (Exception ex) {
	                System.err.println("Error invoking main method: " + ex + " cause = " + ex.getCause());
	              }
	            } else {
	              System.err.println("Main class not found: " + mainClassName);
	            }
	          }

	          this.afterInvokeMain(bundleList);
	          
	        } catch (Exception ex) {
	          System.err.println("Error starting framework: " + ex);
	          ex.printStackTrace();
	        }
	 }
		 

	public static void printStates(List<Bundle> bundleList) {
		for (int i = 0; i < bundleList.size(); i++) {
			Bundle b =   bundleList.get(i);
			System.out.println("bundle id:"+b.getBundleId());
			System.out.println("bundle name:"+b.getSymbolicName());
			System.out.println("bundle status:"+identifyBundleState(b.getState()));
		}
	  }
	  
	  public static String identifyBundleState(int states){
			switch(states){
				case (Bundle.ACTIVE):{
					return "active";
				}
				case (Bundle.UNINSTALLED):{
					return "UNINSTALLED";
				}
				case (Bundle.INSTALLED):{
					return "INSTALLED";
				}
				case (Bundle.RESOLVED):{
					return "RESOLVED";
				}
				case (Bundle.STARTING):{
					return "STARTING";
				}
				case (Bundle.STOPPING):{
					return "STOPPING";
				}
				default:{
					return "unknow";
				}
			}
	  }
	  public static boolean isFragment(Bundle bundle) {
		  
		Boolean result = (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null);
		if(result){
			System.out.println("bundle "+bundle.getSymbolicName()+" is Fragment-Host");
			System.out.println("Fragment-Host: "+bundle.getHeaders().get(Constants.FRAGMENT_HOST));
			System.out.println("bundle id:"+bundle.getBundleId());
			System.out.println("bundle name:"+bundle.getSymbolicName());
			System.out.println("bundle status:"+identifyBundleState(bundle.getState()));
		}
	    return true;
	  }
	
	
	@Test
	public void testHelloworld() throws BundleException{
		final BundleContext context = framework.getBundleContext();
		
		Bundle provider = context.installBundle("file:bundles/provider-3.0.jar");
	    Bundle consumer = context.installBundle("file:bundles/consumer-3.0.jar");

	    provider.start();
	    consumer.start();
	    consumer.stop();
	    provider.stop();
	}
	
	@After
	public void clearup() throws BundleException{
		framework.stop();
	}

	/**
	 * @return the framework
	 */
	public Felix getFramework() {
		return framework;
	}

	/**
	 * @param framework the framework to set
	 */
	public void setFramework(Felix framework) {
		this.framework = framework;
	}

	protected BundleContext getContext(){
		return framework.getBundleContext();
	}
	
	
}
